#pragma once
#include "Utils.h"
#include <map>

class Node;
class Chain;

bool compareChainsLength(Chain* left, Chain* right);
bool compareChainsSpectrum(Chain* left, Chain* right);

class ChainElement {
private:
	Chain* myChain;
	ChainElement* next;
	Node* myNode;
	int chainPosition;
	unsigned int id;
	bool assigned2Chain;
	bool fromOriginalSpectrum;
	bool wasHead;

	bool connectionWillImprove(ChainElement*, int, int);
	bool canConnectSubchain(ChainElement* otherHead, int originalOlignucLimit, int olignucLength, int chainLengthLimit, int offset = 1);

public:
	ChainElement(Node* node, int chainPos, bool original = true);
	ChainElement(int, int, bool original = true);
	ChainElement* nextElement();
	ChainElement* copy(bool);
	Chain* getChain();
	Node* getNode();
	int setNumbering(int, Chain*);
	int getPosition();
	int assign2Chain(Chain*, int);
	unsigned int getID();
	bool isAssigned();
	bool isFromOriginalSpectrum();
	bool tryConnectSubchain(ChainElement* otherHead, int olignucLength, int olignucLimit, int chainLengthLimit);
	bool getWasHead();
	void setNext(ChainElement*);
	void setChain(Chain*);
	void setWasHead();
	void removeSubchain();
};

class Chain {
private:
	int length;
	int originalSpectrumElemsCount;
	ChainElement* head;
	ChainElement* tail;
public:
	void setElementsCount(int);
	void setTail(ChainElement*);
	void setHead(ChainElement*);
	void setOriginalSpectrumElemsCount(int);
	void printChain(bool simple = false);
	void fixChain();
	void breakChain(int);
	void attachChain(Chain*);
	void cutSurplusSubchain(int originalOlignucLimit, int totalOlignucLimit);
	bool tryConnect(Chain* anotherChain, int offset, int olignucLength, int olignucLimit, int chainLengthLimit);
	int getLength();
	int getOriginalSpectrumElemsCount();
	int elemsAfterLengthCut(int surplusLength);
	int lengthAfterElemsCut(int surplusElemsCount);
	ChainElement* getTail();
	ChainElement* getHead();
	Chain(ChainElement*);
	//~Chain(void);
};

class Node {
private:
	int rank;
	int followingNodesCount;
	bool initialized;
	bool chainCreated;
	unsigned int id;
	vector<ChainElement*> myElements;
	Node** followingNodes;

public:
	Node(unsigned int nodeID, int followersCount);
	~Node(void);
	
	void incrementRank();				//Just for rating purposes
	void init(int followingNodesCount);
	void setNode(int index, Node*);
	void setChainCreated();
	void setLeftNodesCount(int);
	void assign2Chain(ChainElement*);
	int getRank();
	int getFollowingNodesCount();
	bool isInitialized();
	bool isChainCreated();
	unsigned int getID();
	ChainElement* getBestChainElement();
	ChainElement* findLoop(Node*, ChainElement* &);
	Node* getNode(int index);
};

class BothPosNeg
{
private:
	CaseData* caseData;
	vector<Chain*> chains;
	map<unsigned int, Node*> nodes;
	int olignucLimit;
	int chainLengthLimit;
	unsigned int bestInitial;
	
	void recoverNodes();
	void eraseChain(Chain*);
	void addChain(Chain*);
	void correctChains();
	void connectChains();
	void advancedChainsConnecting();
	bool canImprove(int);
	bool canStillImprove();
	int add2Nodes(unsigned int current, int startPos);				//Podfunkcja w "recoverNodes()"
	ChainElement* createChains(Node*);

public:
	BothPosNeg(CaseData* caseData);
	~BothPosNeg(void);
};